package com.ttpai.boot.lbs.service;

import com.ttpai.boot.lbs.model.RoutePlanReportVO;
import com.ttpai.boot.lbs.util.CalendarUtil;
import com.ttpai.boot.lbs.util.LLUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * 1. 同一个坐标(坐标间相差1公里以内)忽略时间
 * 2. 时间点变成时间段，可以迟到1个小时
 * <p>
 * Created by kail on 2017/2/24.
 */
@Service
public class DynamicRoutePlanService2 {

    private static final Logger logger = LoggerFactory.getLogger(DynamicRoutePlanService2.class);

    private static final int CHECK_USE_MIN = 29; // 检测一辆车需要多长时间
    private static final int CHECK_LATE_MIN = 60; // 可以迟到多长时间

    @Autowired
    private RoutePlanReportService reportService;

    public Map<String, List<RoutePlanReportVO>> buildDynamicPlain(Date date, String city) {
        List<List<RoutePlanReportVO>> doPlanNotFormat = this.doPlanNotFormat(date, city);
        Map<String, List<RoutePlanReportVO>> plan = CommonService.buildDynamicPlain(doPlanNotFormat);
        buildOnePointToOtherPoint(plan);
        return plan;
    }

    private List<List<RoutePlanReportVO>> doPlanNotFormat(Date date, String city) {

        List<RoutePlanReportVO> reportVOS = reportService.selectRoutePlanReportByDate(date, city);


        List<List<RoutePlanReportVO>> planRoutes = new ArrayList<>();

        for (RoutePlanReportVO startPoint : reportVOS) {
            if (null == startPoint.getLng() || startPoint.getLng().equals(0D)) {
                continue;
            }
            // 从第一个开始，如果没有被分配，则该点为起点
            if (!startPoint.getZhanyong()) {
                startPoint.setEndCheckTime(CalendarUtil.add(startPoint.getCheckTime(), CHECK_USE_MIN, Calendar.MINUTE));

                ArrayList<RoutePlanReportVO> planLine = new ArrayList<>();
                planLine.add(startPoint);
                planRoutes.add(planLine);

                // 从起点开始查找下一条路线
                findNextPointFromStart(planLine, startPoint, reportVOS);

            }
        }
        return planRoutes;
    }

    /**
     * @param line       当前路线
     * @param startPoint 开始路线
     * @param plainDatas 原始数据
     */
    static void findNextPointFromStart(List<RoutePlanReportVO> line, RoutePlanReportVO startPoint, List<RoutePlanReportVO> plainDatas) {
        startPoint.setZhanyong(true); // 标记起始点为占用状态

        // 遍历所有原始数据，判断下一条路线
        for (int i = 0; i < plainDatas.size(); i++) {
            RoutePlanReportVO curPoint = plainDatas.get(i);
            if (null == curPoint.getLng() || curPoint.getLng().equals(0D)) {
                continue;
            }
            // 起始点的检测结束时间
            Date checkEndTime = startPoint.getEndCheckTime();
            // 开始点的检测结束时间为启程时间
            long starTime = checkEndTime.getTime();

            Date lastEndTime = CalendarUtil.add(curPoint.getCheckTime(), CHECK_LATE_MIN, Calendar.MINUTE);// 起点的结束时间 不能比 下一个点的检测时间+1小时还大

            // 没有被占用，并且 检测时间是在第一辆车检测完成之后
            if (!curPoint.getZhanyong() && !lastEndTime.before(checkEndTime)) {
                RoutePlanReportVO nextPoint = null; // 找出下一个点

                Long minCost = Long.MAX_VALUE; // 找到下一点的做小成本 = 消除距离后的时间差(时间最短) + 路程耗时(距离最短)
                for (int j = i; j < plainDatas.size(); j++) {
                    RoutePlanReportVO endPoint = plainDatas.get(j);
                    if (!endPoint.getZhanyong()) {
                        // 两个距离直接需要的秒数
                        double sec = LLUtil.rightAngleDistance2Sec(startPoint.getLng(), startPoint.getLat(), endPoint.getLng(), endPoint.getLat());

                        // 结束时间 - 路程所需时间，消除路程所需时间
                        long endTime = CalendarUtil.add(CalendarUtil.add(endPoint.getCheckTime(), CHECK_LATE_MIN, Calendar.MINUTE), -(int) sec, Calendar.SECOND).getTime();

                        if (starTime < endTime) {
                            // 消除距离后的时间差(时间最短) + 路程耗时(距离最短，距离权重大于时间全总)
                            long curCost = (endTime - starTime) / 1000 + (long) sec * 4;
                            if (curCost < minCost) {
                                minCost = curCost; //
                                nextPoint = endPoint;
                            }
                        }
                    }
                }

                // 遍历所有点，如果找的话就继续，找不到的话不找了
                if (null != nextPoint) {
                    line.add(nextPoint); // 放入路线中
                    Date date1 = CalendarUtil.add(nextPoint.getCheckTime(), CHECK_LATE_MIN, Calendar.MINUTE);
                    Date date2 = CalendarUtil.add(line.get(0).getCheckTime(), line.size() * CHECK_USE_MIN, Calendar.MINUTE);

                    Date nextDate = date1.after(date2) ? date1 : date2;

                    nextPoint.setEndCheckTime(nextDate);
                    findNextPointFromStart(line, nextPoint, plainDatas); // 找的的点作为起始点，开始找下一个
                    break; // 没找到下一个，就不找了，开始计算下一条路线
                }
            }
        }
    }

    /**
     * 把单点归都其他点上
     */
    static void buildOnePointToOtherPoint(Map<String, List<RoutePlanReportVO>> plan) {
        List<String> onePointLine = new ArrayList<>();
        for (Map.Entry<String, List<RoutePlanReportVO>> entry : plan.entrySet()) {
            if (entry.getValue().size() <= 2) {
                onePointLine.add(entry.getKey());
            }
        }

        for (String lineName : onePointLine) {
            ss(onePointLine, lineName, plan);
        }
    }

    private static void ss(List<String> onePointLine, String lineName, Map<String, List<RoutePlanReportVO>> allLine) {
        List<RoutePlanReportVO> singleLine = allLine.get(lineName);
        int findCount = 0;
        for (RoutePlanReportVO singlePoint : singleLine) {

            double minDis = Long.MAX_VALUE;
            String minLine = null;
            int minLineIndex = 0;

            for (Map.Entry<String, List<RoutePlanReportVO>> entry : allLine.entrySet()) {
                // 单线不跟单线合并
                if (!onePointLine.contains(entry.getKey())) {
                    List<RoutePlanReportVO> value = entry.getValue();
                    for (int i = 0; i < value.size(); i++) {
                        RoutePlanReportVO v = value.get(i);
                        if (v.getCheckTime().before(singlePoint.getCheckTime())) {
                            double linearDistance = LLUtil.getLinearDistance(singlePoint.getLng(), singlePoint.getLat(), v.getLng(), v.getLat()) * 5000 * 1000 + Math.abs(v.getCheckTime().getTime() - singlePoint.getCheckTime().getTime());
                            if (linearDistance < minDis) {
                                minLine = entry.getKey();
                                minDis = linearDistance;
                                minLineIndex = i;
                            }
                        }
                    }

                }
            }
            // 找到了最小的Line
            if (StringUtils.isNotBlank(minLine)) {
                singlePoint.setAddress(singlePoint.getAddress() + "【【单点(一条线路不到三个任务)合并】】");
                singlePoint.setCheckTime(CalendarUtil.add(singlePoint.getCheckTime(), 7, Calendar.MINUTE));
                allLine.get(minLine).add(minLineIndex + 1, singlePoint);
                findCount++;
            }
        }
        allLine.remove(lineName);
    }
}
